package com.nx.platform.es.biz.query.search.handler;


import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.nx.platform.es.bean.modle.param.ParamField;
import com.nx.platform.es.bean.modle.query.QueryField;
import com.nx.platform.es.bean.modle.rescore.RescoreField;
import com.nx.platform.es.bean.modle.score.ScoreFunctionField;
import com.nx.platform.es.bean.modle.sort.SortField;
import com.nx.platform.es.biz.query.search.SearchRequestContext;
import com.nx.platform.es.biz.wrapper.parser.StatementOperator;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
import org.elasticsearch.search.rescore.RescorerBuilder;
import org.elasticsearch.search.sort.SortBuilder;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import static com.nx.platform.es.biz.wrapper.parser.StatementOperator.EQUAL;
import static com.nx.platform.es.biz.wrapper.parser.StatementOperator.NOT_EQUAL;


/**
 * @author
 * @since 2016年10月15日
 */
public enum SearchQueryHandler implements SearchRequestHandler {

    INSTANCE;

    @Override
    public void handle(SearchRequestContext context) {

        SearchRequest request = context.getRequest();

        // Query
        QueryBuilder queryBuilder = null;

        // 过滤查询拼接
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        Table<String, StatementOperator, List<String>> queryTable = context.getQueryTable();
        List<QueryField> queryFields = context.getQueryFields();
        for (QueryField queryField : queryFields) {
            ListMultimap<Boolean, QueryBuilder> queryBuilders = LinkedListMultimap.create();
            // 正向条件
            List<String> positives = queryTable.get(queryField.getFace(), EQUAL);
            if (CollectionUtils.isNotEmpty(positives)) {
                for (String positive : positives) {
                    queryField.handle(context, EQUAL, positive, queryBuilders);
                }
            } else {
                queryField.handle(context, EQUAL, null, queryBuilders);
            }
            // 逆向条件
            List<String> negatives = queryTable.get(queryField.getFace(), NOT_EQUAL);
            if (CollectionUtils.isNotEmpty(negatives)) {
                for (String negative : negatives) {
                    queryField.handle(context, NOT_EQUAL, negative, queryBuilders);
                }
            }
            //
            for (Entry<Boolean, QueryBuilder> entry : queryBuilders.entries()) {
                if (Boolean.TRUE.equals(entry.getKey())) {
                    switch (queryField.getType()) {
                    case StringMatch:
                    case StringMultiMatch:
                    case NumberDisMax:
                        boolQueryBuilder.must(entry.getValue());
                        break;
                    case StringMatchShould:
                    case StringMultiMatchShould:
                        boolQueryBuilder.should(entry.getValue());
                        boolQueryBuilder.minimumShouldMatch(1);
                        break;
                    default:
                        boolQueryBuilder.filter(entry.getValue());
                        break;
                    }
                } else {
                    switch (queryField.getType()) {
                    case StringMatchShould:
                    case StringMultiMatchShould:
                        break;
                    default:
                        boolQueryBuilder.mustNot(entry.getValue());
                        break;
                    }
                }
            }
        }

        // 检查过滤查询
        if (boolQueryBuilder.hasClauses()) {
            queryBuilder = boolQueryBuilder;
        }

        // 打分参数
        Map<String, Object> paramsMap = Maps.newHashMap();
        Table<String, StatementOperator, List<String>> paramsTable = context.getParamsTable();
        List<ParamField> paramFields = context.getParamFields();
        if (CollectionUtils.isNotEmpty(paramFields) && paramsTable != null && !paramsTable.isEmpty()) {
            for (ParamField paramField : paramFields) {
                paramField.handle(context, queryTable, paramsTable, paramsMap);
            }
        }

        // 打分函数
        List<ScoreFunctionField> scoreFunctionFields = context.getScoreFunctionFields();
        if (CollectionUtils.isNotEmpty(scoreFunctionFields) && queryBuilder != null) {
            List<FilterFunctionBuilder> functions = new LinkedList<>();
            for (ScoreFunctionField scoreFunctionField : scoreFunctionFields) {
                FilterFunctionBuilder function = scoreFunctionField.handle(paramsMap);
                if (function != null) {
                    functions.add(function);
                }
            }
            if (!functions.isEmpty()) {
                FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(queryBuilder,
                        functions.toArray(new FilterFunctionBuilder[functions.size()]));
                String boostMode = StringUtils.defaultIfEmpty(context.getBoostMode(), "sum");
                String scoreMode = StringUtils.defaultIfEmpty(context.getScoreMode(), "sum");
                queryBuilder = functionScoreQuery
                        .boostMode(CombineFunction.fromString(boostMode))
                        .scoreMode(FunctionScoreQuery.ScoreMode.fromString(scoreMode));
            }
        }

        // 重排序函数
        List<RescoreField> rescoreFields = context.getRescoreFields();
        if (CollectionUtils.isNotEmpty(rescoreFields)) {
            for (RescoreField rescoreField : rescoreFields) {
                RescorerBuilder<?> rescorer = rescoreField.handle(paramsMap);
                if (rescorer != null) {
                    if (rescoreField.getWindowSize() > 0) {
                        rescorer.windowSize(rescoreField.getWindowSize());
                        request.source().addRescorer(rescorer);
                    } else {
                        request.source().addRescorer(rescorer);
                    }
                }
            }
        }

        // 排序
        List<SortField> sortFields = context.getSortFields();
        if (CollectionUtils.isNotEmpty(sortFields)) {
            for (SortField sortField : sortFields) {
                SortBuilder<?> sortBuilder = sortField.handle(paramsMap, context.getSortOrder());
                if (sortBuilder != null) {
                    request.source().sort(sortBuilder);
                }
            }
        }

        // Query为空，默认查询所有数据
        if (queryBuilder == null) {
            queryBuilder = QueryBuilders.matchAllQuery();
        }

        if (context.isUseRequestCache()) {
            request.requestCache(true);
        }

        request.source().timeout(context.getTimeout()).query(queryBuilder);
    }

}
